import {
  LabelActor,
  BRenderer,
  VTActor,
  VTActorRenderType,
  worldToDisplay,
  TLine,
  calculatePolygonCenter,
  PointerActor,
} from "cax-base";
import { TFace } from "@/FrontProcessing/Actors/TFace";
import { TMeshPoint, TPoint } from "@/FrontProcessing/Actors/TMeshPoint";
import { FrontProcessing, MeshEdgeData } from "../";
import { Vector2, Vector3 } from "stl-typescript";
import { MeasurementComp } from "../Measure/MeasurementComp";

interface BoxInfo {
  x: number;
  y: number;
  width: number;
  height: number;
}
// mark
// 网格专用Actor
export class MeshActor extends VTActor {
  faceData: TFace;
  faceColor: number[];
  cellIds = new Set<number>(); // 被选中的cellId
  cellId2label = new Map<number, LabelActor>();
  meshPointData: TMeshPoint;
  meshColor: number[];
  meshId2label = new Map<number, LabelActor>();
  partId: number;
  parent: FrontProcessing | undefined;
  isDpMesh: boolean;
  static readonly unselectedColor = [255, 255, 0, 0];
  static readonly selectedColor = [0, 0, 255, 125];
  static readonly unselectedPointColor = [102, 205, 170, 0];
  static readonly selectedPointColor = [1, 0, 0];
  // * 标记为网格
  readonly MESH_ACTOR = true;
  // * 记录格宇生成的法向量
  gyNormalsInfo = new Map<number, number[]>();
  // * 网格单元中心点
  cellCenterArr: Vector3[] = [];
  constructor(
    faceData_ins: TFace,
    lineWidth: number,
    rgb: [number, number, number],
    isDpMesh: boolean
  ) {
    const options = { initialScalarValue: MeshActor.unselectedColor }; // 网格颜色是透明色
    super(faceData_ins.polyData, options);
    this.faceColor = options.initialScalarValue;
    this.meshColor = options.initialScalarValue;
    this.faceData = faceData_ins;
    if (this.faceData.data.length == 0) {
      alert("网格数据为空");
      this.partId = -1;
    } else {
      this.partId = this.faceData.data[0].partId; // 将第一个网格的partId作为网格单元集合的part id。
    }
    this.meshPointData = new TMeshPoint(faceData_ins); // 设置网格交叉点
    this.setRenderType(VTActorRenderType.SURFACE_WITH_EDGE);
    this.preBuildCellCenter();
    this.instance.getProperty().setEdgeColor(rgb); // 网格边设置为蓝色
    this.instance.getProperty().setLineWidth(lineWidth); // 设置线条粗细，否则看不清楚
    this.instance.getProperty().setOpacity(0.5);
    this.instance.getProperty().setLighting(true); // * 纯色模式
    this.isDpMesh = isDpMesh; // 是否是重复网格
  }

  setParent(p: FrontProcessing) {
    this.parent = p;
  }
  getPartIdByMeshActor() {
    return this.partId;
  }
  containsCellId(cellId: number) {
    return this.faceData.containsCellId(cellId);
  }
  getCells(cellId: number) {
    return this.faceData.getCellsFromFaceData(cellId);
  }
  searchMeshEdgeByPoint(point: any, cell: any) {
    const minDistanceArray = [];
    let minDistance = Infinity;
    const { nodes, cellId, partId } = cell;
    if (nodes.length > 0) {
      for (let j = 0; j < nodes.length; j++) {
        const currentIndex = j;
        const nextIndex = (j + 1) % nodes.length;
        const tempLine = new TLine(
          new Vector3(...nodes[currentIndex].slice(1)),
          new Vector3(...nodes[nextIndex].slice(1))
        );
        const min = tempLine.distanceFromPoint(new Vector3(...point));
        minDistance = Math.min(minDistance, min);
        const id1 = nodes[currentIndex][0];
        const id2 = nodes[nextIndex][0];
        minDistanceArray.push({
          uid: `${Math.min(id1, id2)}_${Math.max(id1, id2)}`,
          minDistance,
          cellId,
          partId,
          start: nodes[currentIndex],
          end: nodes[nextIndex],
          elementEdgeNodeId: nodes[currentIndex][0],
          elementEdgeSecondNodeId: nodes[nextIndex][0],
        });
      }
      const minEdge = minDistanceArray.sort(
        (a, b) => a.minDistance - b.minDistance
      )[0];
      // console.log("minEdge", minEdge);
      return minEdge;
    }
  }
  /**
   * @abstract 通过比较网格点与鼠标点击位置，
   * @returns 返回值为最近网格点的坐标
   * @param vtPickerPos 鼠标位置
   * @param pickedCellId 距离鼠标位置最近网格的ID
   */
  nearestPoint(vtPickerPos: any): TPoint {
    const pointList = this.meshPointData.data;

    const b = new Vector3(...vtPickerPos);
    let minDistance = Infinity;
    let nearestPoint: TPoint = pointList[0];

    for (const point of pointList) {
      const a = new Vector3(point.x, point.y, point.z);
      const distance = a.distanceTo(b);
      if (distance < minDistance) {
        minDistance = distance;
        nearestPoint = point;
      }
    }
    return nearestPoint;
  }
  containsMeshPointId(meshPointId: number) {
    return this.meshPointData.containsMeshPointId(meshPointId);
  }
  /**
   * 获取 meshId 对应的 elemId(GyCellId)
   */
  getElemIdByCellId(meshId: number) {
    return this.faceData.getGyCellId(meshId);
  }
  removeMeshActor(id: string) {
    if (this.parent) {
      this.parent.measureComp.removeMeshActor(id);
    }
  }
  coverMeshEdgeColor(
    data: MeshEdgeData,
    startPt: [number, number, number],
    endPt: [number, number, number],
    color: [number, number, number],
    lineWidth: number
  ) {
    if (this.parent) {
      // const measure = new MeasurementComp(this.parent);
      this.parent.measureComp.coverMeshLine(
        data,
        startPt,
        endPt,
        color,
        lineWidth
      );
    }
  }
  setMeshEdgeColor(
    data: MeshEdgeData,
    startPt: [number, number, number],
    endPt: [number, number, number],
    color: [number, number, number],
    lineWidth: number
  ) {
    // add measureActors  clearMeasureActors
    if (this.parent) {
      // const measure = new MeasurementComp(this.parent);
      this.parent.measureComp.createMeshLine(
        data,
        startPt,
        endPt,
        color,
        lineWidth
      );
    }
  }
  clearMeshEdge() {
    if (this.parent) {
      this.parent.measureComp.clearMeasureActors();
    }
  }
  setMeshElementColor(color = [255, 255, 0, 255]) {
    Array.from(this.cellIds).forEach((cellId: number) => {
      this.setCellColor(cellId, color);
    });
    this.cellIds = new Set<number>();
  }
  // 设置取消选中 《网格》 功能，二次点击取消选中
  selectCellWithDoubleClick(cellId: number) {
    if (!this.cellIds.has(cellId)) {
      this.cellIds.add(cellId);
      this.setCellColor(cellId, MeshActor.selectedColor);
    } else {
      // 再次选中时，要取消选中。
      this.cellIds.delete(cellId);
      this.setCellColor(cellId, MeshActor.unselectedColor);
    }
  }
  // 设置取消选中 《网格点》 功能，二次点击取消选中
  selectNodeWithDoubleClick(point: any) {
    const TPointIndex: number = this.meshPointData.data.findIndex((value) => {
      return value === point;
    });
    const isSelected =
      this.parent?.selectBoxComp.selectedPoints.findIndex((value) => {
        return value === point;
      }) === -1;
    if (isSelected) {
      const pointerActor = new PointerActor([point.x, point.y, point.z], {
        color: [1, 0, 0],
        radius: 1,
      });
      pointerActor.userdata.id = point.nodeId;
      this.parent?.model.add(pointerActor);
      if (this.parent && this.parent.selectBoxComp) {
        this.parent.selectBoxComp.selectedPoints =
          this.parent.selectBoxComp.selectedPoints.concat(
            this.meshPointData.data[TPointIndex]
          );
      }
    } else {
      this.parent?.system?.app.graphicActorList.value.map((gActor) => {
        if (
          gActor instanceof PointerActor &&
          gActor.userdata.id === point?.nodeId
        ) {
          this.parent?.model.remove(gActor);
        }
      });

      if (this.parent && this.parent.selectBoxComp) {
        this.parent.selectBoxComp.selectedPoints =
          this.parent.selectBoxComp.selectedPoints.filter(
            (item) => item !== this.meshPointData.data[TPointIndex]
          );
      }
    }
  }

  /**
   * @function selectMesh 框选选中网格，只处理还未被选中网格
   * @param cellId 网格的ID
   * @param index 网格的格宇索引
   * */
  selectMesh(cellId: number, index: number) {
    this.parent?.selector.selectMeshByBox(index);
    if (!this.cellIds.has(cellId)) {
      this.cellIds.add(cellId);
      this.setCellColor(cellId, MeshActor.selectedColor);
    }
  }

  // 选中网格交叉点(仅用于框选)
  selectMeshPoint(TPointIndex: number) {
    const point: TPoint = this.meshPointData.data[TPointIndex];
    const isSelected =
      this.parent?.selectBoxComp.selectedPoints.findIndex((value) => {
        return value.nodeId === point.nodeId;
      }) === -1;
    if (isSelected) {
      const pointerActor = new PointerActor([point.x, point.y, point.z], {
        color: [1, 0, 0], // 框选时的点的颜色为红色
        radius: 1, // 框选时的点的半径为1
      });
      pointerActor.userdata.id = point.nodeId;
      this.parent?.model.add(pointerActor);
      if (this.parent && this.parent.selectBoxComp) {
        this.parent.selectBoxComp.selectedPoints =
          this.parent.selectBoxComp.selectedPoints.concat(
            this.meshPointData.data[TPointIndex]
          );
      }
    }
  }

  unselectAllElements() {
    this.removeCellIds();
    this.cellIds.forEach((cellId) => {
      this.setCellColor(cellId, MeshActor.unselectedColor);
    });
    this.cellIds.clear();
  }
  unselectAllMeshPoints() {
    this.removeMeshPointIds();
    // this.meshPointData.data.forEach((meshPointId: TPoint) => {
    //   this.parent?.model.remove(this.meshPointData.pointsActor[meshPointId]);
    // });
  }

  showCellIds() {
    console.log("cellIds:", this.cellIds);
    this.cellIds.forEach((cellId) => {
      // 添加网格单元标签
      const gyCellId = this.faceData.getGyCellId(cellId);
      const cellPos = this.faceData.getCellPos(cellId);
      const cellLabel = new LabelActor([cellPos.x, cellPos.y, cellPos.z], {
        text: gyCellId.toString(),
      });
      this.parent?.model.add(cellLabel);
      this.cellId2label.set(cellId, cellLabel);
    });
    return;
  }

  // 清除所有的cell id的标签
  removeCellIds() {
    this.cellId2label.forEach((value) => {
      this.parent?.model.remove(value);
    });

    this.cellId2label.clear();
  }
  // 清除所有的mesh id的标签
  removeMeshPointIds() {
    this.meshId2label.forEach((value) => {
      this.parent?.model.remove(value);
    });
    this.meshId2label.clear();
  }

  // 通过选择框选中网格单元
  selectElementsByBox(
    x: number,
    y: number,
    width: number,
    height: number,
    containerHeight: number,
    renderer: BRenderer,
    engine: FrontProcessing
  ) {
    // 现在需要拿到网格单元数据，逐个遍历了。
    const boxInfo: BoxInfo = {
      x: x,
      y: y,
      width: width,
      height: height,
    };
    for (let cellId = 0; cellId < this.faceData.data.length; cellId++) {
      const pointsPos = this.getPointsPos(cellId, renderer, containerHeight);
      if (this.selectBoxContainsElement(pointsPos, boxInfo)) {
        const index = this.getElemIdByCellId(cellId);
        this.selectMesh(cellId, index);
        engine.render.render();
      }
    }
  }

  // 通过选择框选中网格交叉点
  selectMeshPointByBox(
    x: number,
    y: number,
    width: number,
    height: number,
    containerHeight: number,
    renderer: BRenderer,
    engine: FrontProcessing
  ) {
    // 现在需要拿到网格单元数据，逐个遍历了。
    const boxInfo: BoxInfo = {
      x: x,
      y: y,
      width: width,
      height: height,
    };
    console.log("boxInfo", boxInfo);
    for (let i = 0; i < this.meshPointData.data.length; i++) {
      const pointX = this.meshPointData.data[i].x;
      const pointY = this.meshPointData.data[i].y;
      const pointZ = this.meshPointData.data[i].z;
      const r = worldToDisplay(renderer, pointX, pointY, pointZ);
      const ap = window.devicePixelRatio;
      const leftOffset = 368;
      const absX = r[0] / ap + leftOffset;
      const topOffset = 56;
      const absY = containerHeight - r[1] / ap + topOffset;
      if (this.isPointInBox(absX, absY, boxInfo)) {
        this.selectMeshPoint(i);
        engine.render.render();
      }
    }
    engine.render.render();
  }

  // 通过i下标拿到各个点在浏览器中的绝对坐标(absX, absY)
  getPointsPos(i: number, renderer: BRenderer, containerHeight: number) {
    const cellData = this.faceData.data;
    const num = cellData[i].componentsNumber;
    const pointsPos: number[][] = [];
    for (let n = 0; n < num; n++) {
      const pointX = cellData[i].nodes[n][1];
      const pointY = cellData[i].nodes[n][2];
      const pointZ = cellData[i].nodes[n][3];
      const r = worldToDisplay(renderer, pointX, pointY, pointZ);
      const ap = window.devicePixelRatio;
      const leftOffset = 368;
      const absX = r[0] / ap + leftOffset;
      const topOffset = 56;
      const absY = containerHeight - r[1] / ap + topOffset;
      pointsPos.push([absX, absY]);
    }

    return pointsPos;
  }

  // 判断选择框是否包含了mesh
  selectBoxContainsElement(pointsPos: number[][], boxInfo: BoxInfo) {
    return (
      this.containCenter(pointsPos, boxInfo) ||
      this.containPoint(pointsPos, boxInfo) ||
      this.elementContainsBoxPoint(pointsPos, boxInfo)
    );
  }

  // 判断选择框的四个点是否在网格内
  elementContainsBoxPoint(pointsPos: number[][], boxInfo: BoxInfo) {
    const boxPoints: number[][] = [];
    boxPoints.push([boxInfo.x, boxInfo.y]);
    boxPoints.push([boxInfo.x + boxInfo.width, boxInfo.y]);
    boxPoints.push([boxInfo.x, boxInfo.y + boxInfo.height]);
    boxPoints.push([boxInfo.x + boxInfo.width, boxInfo.y + boxInfo.height]);

    for (let i = 0; i < 4; i++) {
      if (this.isPointInElement(boxPoints[i], pointsPos)) {
        return true;
      }
    }
    return false;
  }

  // 判断选择框的单个点是否在网格内
  isPointInElement(boxPoint: number[], elementPoints: number[][]) {
    if (elementPoints.length == 3) {
      return this.isPointInTriangle(
        boxPoint,
        elementPoints[0],
        elementPoints[1],
        elementPoints[2]
      );
    } else if (elementPoints.length == 4) {
      return (
        this.isPointInTriangle(
          boxPoint,
          elementPoints[0],
          elementPoints[1],
          elementPoints[2]
        ) ||
        this.isPointInTriangle(
          boxPoint,
          elementPoints[2],
          elementPoints[3],
          elementPoints[0]
        )
      );
    }
    return false;
  }

  // 判断三个点是否在三角形内
  isPointInTriangle(p: number[], a: number[], b: number[], c: number[]) {
    const triPoints: number[][] = [];
    triPoints.push(a);
    triPoints.push(b);
    triPoints.push(c);
    triPoints.push(a);
    let flag = 0;
    for (let i = 0; i < 3; i++) {
      const ab = new Vector2(triPoints[i + 1][0], triPoints[i + 1][1]);
      const aVector = new Vector2(triPoints[i][0], triPoints[i][1]);
      ab.sub(aVector);
      const ap = new Vector2(p[0], p[1]);
      ap.sub(aVector);
      const crossResult = ap.cross(ab);
      if (flag == 0) {
        flag = crossResult;
      }
      if (flag * crossResult < 0) return false;
    }

    return true;
  }

  // 选择范围是否包含中心点
  containCenter(pointsPos: number[][], boxInfo: BoxInfo) {
    const centerPos = this.computeCenterPos(pointsPos);
    const absX = centerPos.x;
    const absY = centerPos.y;
    if (this.isPointInBox(absX, absY, boxInfo)) {
      return true;
    }
    return false;
  }

  // 计算中心点的坐标
  computeCenterPos(pointsPos: number[][]) {
    let x = 0;
    let y = 0;
    const n = pointsPos.length;
    for (let i = 0; i < n; i++) {
      x += pointsPos[i][0];
      y += pointsPos[i][1];
    }
    return { x: x / n, y: y / n };
  }

  // 选择框范围是否包含了几个点。
  containPoint(pointsPos: number[][], boxInfo: BoxInfo) {
    for (let n = 0; n < pointsPos.length; n++) {
      const absX = pointsPos[n][0];
      const absY = pointsPos[n][1];
      if (this.isPointInBox(absX, absY, boxInfo)) {
        return true;
      }
    }

    return false;
  }

  isPointInBox(absX: number, absY: number, boxInfo: BoxInfo) {
    return (
      absX >= boxInfo.x &&
      absX <= boxInfo.x + boxInfo.width &&
      absY >= boxInfo.y &&
      absY <= boxInfo.y + boxInfo.height
    );
  }

  // 返回被选中的网格id
  getSelectedCellIds(): Set<number> {
    return this.cellIds;
  }

  // * 预先计算网格单元的中心点
  preBuildCellCenter() {
    // 计算单元中心
    const cellVertexes = this.getCellVertexes();
    const centers: Vector3[] = [];
    cellVertexes.forEach((item) => {
      centers.push(calculatePolygonCenter(item));
    });
    this.cellCenterArr = centers;
  }

  // * 转换CellId到索引
  transferCellIdsToIndexes(cellIds: number[]): number[] {
    const result: number[] = [];
    for (let i = 0; i < this.faceData.data.length; i++) {
      if (cellIds.findIndex((t) => t === this.faceData.data[i].cellId) !== -1) {
        result.push(i);
      }
      if (result.length === cellIds.length) {
        break;
      }
    }
    return result;
  }

  //获取面的所有网格id
  getCellIds(): number[] {
    return this.faceData.data.map((item) => item.cellId);
  }
}
